<?php

class Crud_Section_Row
{
	/**
	 * @var Crud_Section
	 */
	protected $_table;
	
	/**
	 * @var Zend_Db_Table_Row
	 */
	protected $_row;
	
	/**
	 * Changed columns array.
	 * 
	 * @var array
	 */
	protected $_changedColumns = array();
	
	public function __construct(Crud_Section $table, Zend_Db_Table_Row_Abstract $row)
	{
		$this->_table = $table;
		$this->_row = $row;
	}
	
	/**
	 * Call wrapped row methods.
	 * 
	 * @param string $method
	 * @param array $args
	 * @return mixed
	 */
	public function __call($method, $args)
	{
		return call_user_func_array(array($this->getRow(), $method), $args);
	}
	
	/**
	 * Return parent Crud_Section.
	 * 
	 * @return Crud_Section
	 */
	public function getTable()
	{
		return $this->_table;
	}
	
	/**
	 * Return wrapped row.
	 * 
	 * @return Zend_Db_Table_Row_Abstract
	 */
	public function getRow()
	{
		return $this->_row;
	}
	
	/**
	 * Return column value.
	 * 
	 * @param string $column
	 * @return mixed
	 */
	public function getColumnValue($column)
	{
		if ($column instanceof Crud_Db_Table_Column) {
			$column = $column->getName();
		}
		return $this->getRow()->__get($column);
	}
	
	/**
	 * Set column value.
	 * 
	 * @param Crud_Section_Column $column
	 * @param mixed $value
	 * @return void
	 */
	public function setColumnValue(Crud_Section_Column $column, $value)
	{
		if ($column instanceof Crud_Db_Table_Column) {
			$column = $column->getName();
		}
		
		return $this->getRow()->__set($column, $value);
	}
	
	/**
	 * Return true when id is not set.
	 * 
	 * @return boolean
	 */
	public function isNew()
	{
		return !$this->getColumnValue('id');
	}
	
    /**
     * Deletes existing rows.
     * Also delete many2many rows.
     * 
     * @return integer Number of affected rows
     */
	public function delete()
	{
		$table = $this->getTable();
		$m2ms = $table->getMany2ManyTables();
		$nm2ms = $table->getNotMany2ManyTables();
		
		foreach ($m2ms as $m2m) {
			$element = new Crud_Form_Element_Many2many($table, $m2m);
			$element->setRowValue($this, array());
		}
		
		foreach ($nm2ms as $nm2m) {
			if ($table->getTableName() == $nm2m->getTableName()) continue;
			$peer = $nm2m->getPeer();
			$column = $nm2m->getFKColumnFor($table)->getKey();
			$rows = $peer->fetchAll("`$column` = '{$this->_row->getId()}'");
			foreach ($rows as $row) {
				$sRow = new Crud_Section_Row($nm2m, $row);
				$sRow->delete();
			}
		}
		
		$this->getRow()->delete();
	}
	
	/**
	 * Process form and update column values.
	 * Also delete old and create new many2many entryes.
	 * 
	 * @param Crud_Form $form
	 * @return void
	 */
	public function setForm(Crud_Form $form)
	{
		$data = $form->getValues();
		$table = $this->getTable();

		// Set value for each column
		foreach ($data as $key => $value) {
			if (!$table->hasColumn($key)) {
				continue;
			}
			
			$crudElement = $form->getCrudElement($key);
			$oldValue = $crudElement->getRowValue($this);
			$crudElement->setRowValue($this, $value);
			$newValue = $crudElement->getRowValue($this);
			
			if ($oldValue != $newValue) {
				$this->_changedColumns[] = $key;
			}
		}
		
		$action = ($this->isNew()) ? 'add entry in' : 'modify';
		$this->save();
		$this->log($action);
	}
	
	/**
	 * Adding to log. 
	 * 
	 * @return void
	 */
	public function log($action = 'modify')
	{
		// Recursieve gone xD
		if ($this->_row instanceof AdminLogRow) {
			return;
		}
		
		$tableName = $this->getTable()->getTableName();
		$columns = implode('; ', $this->_changedColumns);
		$count = count($this->_changedColumns);
		$rowId = $this->getId();
		
		$peer = AdminLogPeer::getInstance();
		$row = $peer->createRow();
		$row->setChangedTable($tableName);
		$row->setChangedColumns($columns);
		$row->setChangedRow($rowId);
		
		
		
		$user = Zend_Auth::getInstance()->getIdentity();
		$user = AdminUserPeer::getInstance()->findByLogin($user)->current();
		
		$tableTag = "<a href='/admin/crud/list/table/$tableName'>$tableName</a>";
		
		if ($action == 'delete') {
			$message = "User `{$user->getLogin()}` delete entry with id `$rowId` from `$tableTag`";
		} else {
			$message = "User `{$user->getLogin()}` $action `$tableTag` (id=<a href='/admin/crud/edit/table/$tableName/id/$rowId'>$rowId</a>)";
			if ($action == 'modify') {
				 $message .= " ($count cols changed)";
			}
		}
		
		$row->setLog($message);
		$row->setUserId($user->getId());
		$row->save();
	}
	
	
	/**
	 * Устанавливает блокировку текущего ряда за переданным пользователем.
	 * Блокировка истекает через 15 минут.
	 * Если вызвать метод повторно - 15 минут начнут отсчитываться заново.
	 * Если блокировка уже закреплена за другим пользователем и истекла - она удаляется.
	 * Если блокировка за другим пользователем еще не истекла - новая не создается,
	 * возвращается все-равно ряд блокировки.
	 * 
	 * @param AdminUserRow $user
	 * @return AdminLockRow
	 */
	public function lock($user) 
	{
		// Пытаемся получить существующую блокировку для данного row
		$lock = $this->getAdminLockRow();
		
		// Если блокировки ряда нет - просто создаем новую и возвращаем
		if (!$lock) {
			$lock = AdminLockPeer::getInstance()->createRow();
			$lock->setAdminUserId($user->getId());
			$lock->setRowId($this->getId());
			$lock->setSectionId($this->getTable()->getId());
			
			$lock->save();
			return $lock;
		}
		
		// Если блокировка существует - проверям, прошло ли 15 минут с момента created,
		// Или пренадлежит ли она переданному пользователю
		$ago = time() - 15 * 60;
		$created = strtotime($lock->getCreated());
		$isCurrentUser = ($lock->getAdminUserId() == $user->getId());
		if (($created < $ago) || $isCurrentUser) {
			// Если одно из условий выполнено - обновляем блокировку
			$lock->setCreated(date('Y-m-d H:i:s'));
			$lock->setAdminUserId($user->getId());
			$lock->save();
		}
		
		return $lock;
	}
	
	/**
	 * Разблокировка записи.
	 * @return void
	 */
	public function unLock() {
		$lock = $this->getAdminLockRow();
		if ($lock) {
			$lock->delete();
		}
	}
	
	/**
	 * Возвращает объект блокировки текущего ряда, либо null.
	 * 
	 * @return AdminLockRow 
	 */
	public function getAdminLockRow()
	{
		$select = AdminLockPeer::getInstance()->select();
		$select->where(AdminLockPeer::SECTION_ID, $this->getTable()->getId());
		$select->where(AdminLockPeer::ROW_ID, $this->getId());
		$lock = AdminLockPeer::getInstance()->fetchRow($select);
		
		return $lock;
	}
	
	/**
	 * Проверяем, закреплена ли блокировка ряда за текущем пользователем.
	 * 
	 * @param AdminUserRow $user
	 * @return boolean
	 */
	public function isLockedBy($user)
	{
		$lock = $this->getAdminLockRow();
		if (!$lock) {
			return false;
		}
		
		if ($lock->getAdminUserId() == $user->getId()) {
			return true;
		}
		
		return false;
	}
	
}